#include "rtda/heap/cp_interface_methodref.h"
#include "classfile/class_file.hpp"
#include "rtda/heap/method.h"
#include "rtda/heap/constant_pool.hpp"
#include "rtda/heap/class.h"
#include "rtda/heap/method_lookup.h"
#include <memory>

namespace jvm {
namespace heap {

InterfaceMethodRef::InterfaceMethodRef(std::shared_ptr<ConstantPool> pCp,
    const std::shared_ptr<classfile::ConstantMemberRefInfo> pMemberrefInfo) 
    : MemberRef(pCp) {
    copyMemberRefInfo(pMemberrefInfo);
}

std::shared_ptr<InterfaceMethodRef> InterfaceMethodRef::newInterfaceMethodRef(std::shared_ptr<ConstantPool> pCp, 
    const std::shared_ptr<classfile::ConstantInterfaceMethodRefInfo> pInterfaceMethodrefInfo) {
    return std::make_shared<InterfaceMethodRef>(pCp, pInterfaceMethodrefInfo);
}

std::shared_ptr<Method> InterfaceMethodRef::resolvedInterfaceMethod() {
    if (!m_pMethod) {
        resolveInterfaceMethodRef();
    }
    return m_pMethod;
}

void InterfaceMethodRef::resolveInterfaceMethodRef() {
    auto d = m_pCp->getClass();
    auto c = resolvedClass();

    if (!c->isInterface()) {
        throw std::runtime_error("java.lang.IncompatibleClassChangeError");
    }

    auto method = lookupInterfaceMethod(c, m_name, m_descriptor);
    if (!method) {
        throw std::runtime_error("java.lang.NoSuchMethodError");
    }
    if (!method->isAccessibleTo(d)) {
        throw std::runtime_error("java.lang.IllegalAccessError");
    }

    m_pMethod = method;
}

std::shared_ptr<Method> InterfaceMethodRef::lookupInterfaceMethod(std::shared_ptr<Class> pClass, const std::string& name, const std::string& descriptor)
{
    for (auto& method : pClass->getMethods())
    {
        if (method->getName() == name && method->getDescriptor() == descriptor) return method;
    }
    return lookupMethodInInterfaces(pClass->getInterfaces(), name, descriptor);
}

} // namespace heap
} // namespace jvm